package zookeeper

import (
	"context"
	"encoding/json"
	"gitee.com/vrv_media/go-micro-framework/registry"
	regOps "gitee.com/vrv_media/go-micro-framework/registry/options"
	"github.com/go-zookeeper/zk"
	"golang.org/x/sync/singleflight"
	"path"
	"time"
)

type ZookeeperDiscovery struct {
	rootPath  string
	namespace string
	user      string
	password  string
	conn      *zk.Conn
	group     singleflight.Group
}

func NewZookeeperDiscovery(options *regOps.DiscoveryOptions) *ZookeeperDiscovery {
	conn, _, err := zk.Connect([]string{options.Address}, time.Second*15)
	if err != nil {
		panic(err)
	}
	return &ZookeeperDiscovery{
		rootPath: "/microservices",
		conn:     conn,
		group:    singleflight.Group{},
	}
}

// GetService get services from zookeeper
func (d *ZookeeperDiscovery) GetService(ctx context.Context, serviceName string) ([]*registry.ServiceInstance, error) {
	instances, err, _ := d.group.Do(serviceName, func() (interface{}, error) {
		serviceNamePath := path.Join(d.namespace, serviceName)
		servicesID, _, err := d.conn.Children(serviceNamePath)
		if err != nil {
			return nil, err
		}
		items := make([]*registry.ServiceInstance, 0, len(servicesID))
		for _, service := range servicesID {
			servicePath := path.Join(serviceNamePath, service)
			serviceInstanceByte, _, err := d.conn.Get(servicePath)
			if err != nil {
				return nil, err
			}
			item, err := unmarshal(serviceInstanceByte)
			if err != nil {
				return nil, err
			}
			items = append(items, item)
		}
		return items, nil
	})
	if err != nil {
		return nil, err
	}
	return instances.([]*registry.ServiceInstance), nil
}

func (d *ZookeeperDiscovery) Watch(ctx context.Context, serviceName string) (registry.Watcher, error) {
	prefix := path.Join(d.namespace, serviceName)
	return newWatcher(ctx, prefix, serviceName, d.conn)
}

func unmarshal(data []byte) (si *registry.ServiceInstance, err error) {
	err = json.Unmarshal(data, &si)
	return
}
